package com.hsmw.api.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hsmw.api.config.jwt.CurrentHsmwUserUtils;
import com.hsmw.api.config.jwt.LoginUser;
import com.hsmw.api.mapper.SysOperLogMapper;
import com.hsmw.api.service.HsmwAuditService;
import com.hsmw.api.service.SysOperLogService;
import com.hsmw.common.Log;
import com.hsmw.domain.HsmwUser;
import com.hsmw.domain.SysOperLog;
import com.htn.common.core.base.BusinessStatus;
import com.htn.common.core.utils.SecurityUtils;
import com.htn.common.core.utils.ServletUtils;
import com.htn.common.core.utils.ip.IpUtils;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/*
*
 * 统一日志处理切面
 * Created by xc on 190903.

*/

@Aspect
@Component
@Order(1)
public class WebLogAspect {

    @Autowired
    private SysOperLogService sysOperLogService;

    @Autowired
    private CurrentHsmwUserUtils currentHsmwUserUtils;

    private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);

    @Pointcut("@annotation(com.hsmw.common.base.Log)")
    public void webLog() {
    }



    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    }

    @AfterReturning(pointcut = "webLog()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) throws Throwable {
        handleLog(joinPoint, null, jsonResult);
    }
    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "webLog()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e){
        handleLog( joinPoint, e, null);
    }



/*

    @Around("webLog()")
    public void doAround(ProceedingJoinPoint joinPoint,final Exception e, Object jsonResult) throws Throwable {
        handleLog(joinPoint, null, jsonResult);
    }
*/


    protected void handleLog(JoinPoint joinPoint,final Exception e, Object jsonResult){
        try
        {
            // 获得注解
            String  title = getAnnotationTitle(joinPoint);
            long startTime = System.currentTimeMillis();
            //获取当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            LoginUser user = currentHsmwUserUtils.getCurrentUserInfo();
            //记录请求信息
            SysOperLog webLog = new SysOperLog();
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            webLog.setOperIp(ip);
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                webLog.setOperName(apiOperation.value());
            }
            Object result = new Object();
            if (e != null)
            {
                webLog.setStatus(BusinessStatus.FAIL.ordinal());
                webLog.setErrorMsg(com.htn.common.core.utils.StringUtils.substring(e.getMessage(), 0, 2000));
            }else {
                webLog.setJsonResult(JSON.toJSONString(jsonResult));
            }
            // 返回参数
            long endTime = System.currentTimeMillis();
            String urlStr = request.getRequestURL().toString();
            webLog.setOperLocation(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
            //webLog.setId(request.getRemoteUser());
            webLog.setMethod(request.getMethod());
            webLog.setOperUserId(user.getId());
            webLog.setOperUrl(request.getRequestURL().toString());
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            webLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            webLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            webLog.setOperName(user.getUsername());
            //webLog.setOperUserId();
            webLog.setTitle(title);
            webLog.setOperTime(LocalDateTime.now());
            sysOperLogService.save(webLog);

        }
        catch (Exception exp)
        {
            exp.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

/*

*
     * 根据方法和传入的参数获取请求参数

*/

    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception{
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request，参数和值
        if (log.isSaveRequestData()){
            // 获取参数的信息，传入到数据库中。
            setRequestValue(joinPoint, operLog);
        }
    }

    /**
     * 获取请求的参数，放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception{
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)){
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(params.substring(0, 2000));
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(paramsMap.toString().substring( 0, 2000));
        }
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private String getAnnotationTitle(JoinPoint joinPoint) throws Exception{
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null){
            for(Annotation annotation :method.getDeclaredAnnotations()){
                String str = annotation.toString();
                if(str.contains("com.hsmw.common.base.Log")){
                    JSONObject obj  =JSON.parseObject(JSON.toJSONString(annotation));
                    Object title =obj.get("title");
                    if(StringUtils.isEmpty(title)){
                        return  "";
                    }else {
                        return  title.toString() ;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray){
        String params = "";
        if (paramsArray != null && paramsArray.length > 0){
            for (int i = 0; i < paramsArray.length; i++){
                if (!isFilterObject(paramsArray[i])){
                    Object jsonObj = new Object();
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o){
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }

}
